﻿namespace FSharpChess
module SEE =
    let Move_Blank = Move.blank
    let get_lists moveMade psn =
        let movesFriendly = new ResizeArray<int>()
        movesFriendly.Capacity<-16
        let movesEnemy = new ResizeArray<int>()
        movesEnemy.Capacity<-16
        let col = psn.Pcs.[moveMade.mvPiece].pcIsWhite
        let ord = moveMade.To
        let sq = psn.Sqs.[ord]
        let getmv nm vl ordx =
            let pieceid = psn.Sqs.[ordx]
            if (pieceid <> -1) then
                if psn.Pcs.[pieceid].pcIsWhite = col then
                    if (pieceid <> moveMade.mvPiece && psn.Pcs.[pieceid].Name = nm) then movesFriendly.Add(vl)
                else
                    if (psn.Pcs.[pieceid].Name = nm) then movesEnemy.Add(vl)
        List.iter (getmv enmName.Knight Eval.ValueKnight) Ref.movsN.[ord]
        List.iter (getmv enmName.King Eval.ValueKing) Ref.movsK.[ord]
        let getmvPF (off) =
            let pieceid = psn.Sqs.[off]
            if (pieceid <> -1 && psn.Pcs.[pieceid].pcIsWhite = col && pieceid <> moveMade.mvPiece && psn.Pcs.[pieceid].Name = enmName.Pawn) then movesFriendly.Add(Eval.ValuePawn)
        List.iter getmvPF (if col then Ref.attsPWto.[ord] else Ref.attsPBto.[ord])
        let getmvPE (off) =
            let pieceid = psn.Sqs.[off]
            if (pieceid <> -1 && psn.Pcs.[pieceid].pcIsWhite <> col && psn.Pcs.[pieceid].Name = enmName.Pawn) then movesEnemy.Add(Eval.ValuePawn)
        List.iter getmvPE (if col then Ref.attsPBto.[ord] else Ref.attsPWto.[ord])
        let rec getMovesRays nm r rl vl =
            match r with
                |[] -> if rl<>[] then getMovesRays nm rl.Head rl.Tail vl 
                | r ->  
                    let cord = r.Head 
                    let pieceid =  psn.Sqs.[cord]
                    if pieceid = -1 then
                        getMovesRays nm r.Tail rl vl  
                    elif psn.Pcs.[pieceid].pcIsWhite = col  && (psn.Pcs.[pieceid].Name=nm || psn.Pcs.[pieceid].Name=enmName.Queen ) then
                        if pieceid <> moveMade.mvPiece then movesFriendly.Add(if psn.Pcs.[pieceid].Name=enmName.Queen then Eval.ValueQueen else vl)
                        getMovesRays nm r.Tail rl vl
                    elif psn.Pcs.[pieceid].pcIsWhite <> col && (psn.Pcs.[pieceid].Name=nm || psn.Pcs.[pieceid].Name=enmName.Queen ) then
                        movesEnemy.Add(if psn.Pcs.[pieceid].Name=enmName.Queen then Eval.ValueQueen else vl)
                        getMovesRays nm r.Tail rl vl
                    else
                        if rl<>[] then getMovesRays nm rl.Head rl.Tail vl
        getMovesRays enmName.Rook Ref.raysR.[ord].Head Ref.raysR.[ord].Tail Eval.ValueRook
        getMovesRays enmName.Bishop Ref.raysB.[ord].Head Ref.raysB.[ord].Tail Eval.ValueBishop
        // sort remaining moves by piece value
        movesFriendly.Sort()
        if (movesEnemy.Count > 1) then
            // sort remaining moves by piece value
            movesEnemy.Sort()
        movesFriendly,movesEnemy
    
    let see_move moveMade psn =
        // Static Exchange Evaluator
        let movesFriendly,movesEnemy = get_lists moveMade psn
        let mvFprom = if moveMade.mvPiece<16 && Ref.ri.[moveMade.To]=8||moveMade.mvPiece>15 && Ref.ri.[moveMade.To]=0 then Eval.ValueQueen else Eval.ValuePawn
        let mvEprom = if moveMade.mvPiece>15 && Ref.ri.[moveMade.To]=8||moveMade.mvPiece<16 && Ref.ri.[moveMade.To]=0 then Eval.ValueQueen else Eval.ValuePawn
        // Functions to make a virtual move from each players move list, in order, until one of the players has no remaining moves
        let rec see_rece i ilmv =
            let ans =
                //do not allow King capture if still captures left for opponent
                if (i <movesEnemy.Count && movesEnemy.[i]=Eval.ValueKing && i<movesFriendly.Count) then
                    0
                elif (i <movesEnemy.Count) then
                    //allow for promotions
                    if movesEnemy.[i]=Eval.ValuePawn then
                        ilmv + mvEprom - Eval.ValuePawn - (see_recf i mvEprom)
                    else
                        ilmv - (see_recf i movesEnemy.[i])
                else
                    0
            if ans > 0 then ans else 0
        and see_recf i ilmv =
            let ans =
                //do not allow King capture if still captures left for opponent
                if (i<movesFriendly.Count && movesFriendly.[i]=Eval.ValueKing && i+1<movesEnemy.Count) then
                    0
                elif (i <movesFriendly.Count) then
                    //allow for promotions
                    if movesFriendly.[i]=Eval.ValuePawn then
                        ilmv + mvFprom - Eval.ValuePawn - (see_recf (i+1) mvFprom)
                    else
                        ilmv - (see_recf (i+1) movesFriendly.[i])
                else
                    0
            if ans > 0 then ans else 0
        let cap_score = if moveMade.pieceCaptured<> -1 then Eval.Value(moveMade.pieceCaptured,psn) else 0
        // allow for promotions
        let promAdd mv =
            match mv.mvName with
                |PawnPromotionQueen -> Eval.ValueQueen - Eval.ValuePawn
                |PawnPromotionRook -> Eval.ValueRook - Eval.ValuePawn
                |PawnPromotionBishop -> Eval.ValueBishop - Eval.ValuePawn
                |PawnPromotionKnight -> Eval.ValueKnight - Eval.ValuePawn
                |_ -> 0
        let promsc = promAdd(moveMade)
        let piece_val = Eval.Value(moveMade.mvPiece,psn) + promsc
        let gain = -(see_rece 0 piece_val)
        //if gain is greater than 0 then enemy would not recapture so need to set to zero
        gain+cap_score+promsc
    

